﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace TestUtilities
{
	public class ActionMock
	{
		private Action action;
		private int callCount = 0;

		public ActionMock()
		{
			this.action = () => Interlocked.Increment(ref this.callCount);
		}

		public Action Action
		{
			get
			{
				return this.action;
			}
		}

		public int CalledTiems
		{
			get
			{
				return this.callCount;
			}
		}
	}

	public class ActionMock<T>
	{
		private Action<T> action;
		private IList<T> callTracker = new List<T>();

		public ActionMock()
		{
			this.action = t => this.callTracker.Add(t);
		}

		public bool CalledWithParam(T argParam, int argCount = 1)
		{
			return this.CallWithParamCount(argParam) == argCount;
		}

		public int CallWithParamCount(T argParam)
		{
			return this.callTracker.Count(cp => AreEqual(argParam, cp));
		}

		public static bool AreEqual(T argExpected, T argActual)
		{
			bool tmpResult = (argExpected == null && argActual == null);
			if (!tmpResult)
			{
				if (argExpected != null)
				{
					tmpResult = argExpected.Equals(argActual);
				}
				else
				{
					tmpResult = argActual.Equals(argExpected);
				}
			}
			return tmpResult;
		}

		public void VerifyCall(T argParam, int argCount = 1)
		{
			int tmpActualCalls = this.CallWithParamCount(argParam);

			if (argCount != tmpActualCalls)
			{
				throw new Exception(String.Format("Unexpected number of action calls. Expected: {0}, Action: {1}", argCount, tmpActualCalls));
			}
		}

		public Action<T> Action
		{
			get
			{
				return this.action;
			}
		}

		public int CalledTiems
		{
			get
			{
				return this.callTracker.Count;
			}
		}
	}

	public class ActionMock<T, V>
	{
		private Action<T, V> action;
		private IList<Tuple<T, V>> callTracker = new List<Tuple<T, V>>();

		public ActionMock()
		{
			this.action = (t, v) => this.callTracker.Add(Tuple.Create(t, v));
		}

		public bool CalledWithParam(T argParam1, V argParam2, int argCount = 1)
		{
			return this.CallWithParamCount(argParam1, argParam2) == argCount;
		}

		public int CallWithParamCount(T argParam1, V argParam2)
		{
			return this.callTracker.Count(cp => ActionMock<T>.AreEqual(argParam1, cp.Item1) && ActionMock<V>.AreEqual(argParam2, cp.Item2));
		}

		public void VerifyCall(T argParam1, V argParam2, int argCount = 1)
		{
			int tmpActualCalls = this.CallWithParamCount(argParam1, argParam2);

			if (argCount != tmpActualCalls)
			{
				throw new Exception(String.Format("Unexpected number of action calls. Expected: {0}, Action: {1}", argCount, tmpActualCalls));
			}
		}

		public Action<T, V> Action
		{
			get
			{
				return this.action;
			}
		}

		public int CalledTiems
		{
			get
			{
				return this.callTracker.Count;
			}
		}
	}
}
